/* Noise demo by El Topo in 2009 */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_mixer.h>

#define WIDTH	640
#define HEIGHT	480
#define DEPTH	32
#define BPP	4 /* bytes per pixel */
#define VIDEOFLAGS SDL_SWSURFACE|SDL_DOUBLEBUF

#define TICKWAIT 40 /* delay mellan skärmar i millisekunder */
#define SCROLLWAIT 20 /* värde för scrollflytt */
#define PICTOP 6 /* sista bilden (zero based) */
#define NOISETOP 7 /* sista noiseytan */
#define SCROLL_Y 450
#define RAMPS 7
#define FALSE 0
#define TRUE 1


enum { RAMP_IT, NOISE, WAIT };

/* Prototypes */
SDL_Surface *MyLoadImage(char *image_path);
SDL_Surface *NoiseGenerator(void);
void Update_Scroller(SDL_Surface *font, SDL_Surface *dest_surface,
		     Uint8 movement);
void StartSound(int channel);

Mix_Chunk *mysound;
char *demotitle = "Noise demo";
unsigned int scrollpos = 0, finscroll = 0;
char scrolltext[] =
  "                                     HOWDY HACKERS! THIS IS EL TOPO ON"
  " THE KEYS. I TRUST ALL OF YOU ARE FEELING GROOVY THIS EVENING?...."
  " WELCOME TO THIS SMALL PRESENTATION! ACTUALLY IT'S MY FIRST DEMO, CODED"
  " IN PURE K&R C SYNTAX FROM THE MING DYNASTY. IT'S MAINLY A FEW SDL TRICKS"
  " I WANTED TO TRY OUT, SOME ALPHA BLENDING AND STUFF LIKE THAT. NOT EXACTLY"
  " GROUNDBREAKING BUT EVERYTHING DOESN'T HAVE TO (AND WILL NOT) BE."
  " YOU MAY HAVE NOTICED A SOMEWHAT GRIM TONE TO THIS PROD, WELL THAT IS"
  " BECAUSE EVERYTHING ASSOCIATED WITH NOISE ESTHETICS HAVE TO HAVE THAT."
  " I'M SORRY, IT'S THE LAW. THE SOUND IS A NOISE \"SONG\" I MADE FOR A"
  " PROJECT OF MINE CALLED PSYKOPEST THAT I PLAY AROUND WITH FROM TIME TO TIME."
  " QUICK GREETINGS FLY OUT TO THE FOLLOWING INDIVIDUALS:"
  " ALTEMARK, DINO, JUCKE, MACX, NAMELESS, SIR GARBAGETRUCK, TAPER (AND ALL"
  " THE OTHER COOL PEOPLE AT THE ANTIDOTE BBS)."
  " INSPIRATIONAL GREETINGS GOES TO TRIAD, SPEEDFISTERS, METALVOTZE AND PS"
  " (WHICH IS NOT A GROUP :-))"
  " PLEASE CALL THE FOLLOWING FINE BOARDS:"
  "   ***   THE ANTIDOTE TELNET://ANTIDOTE.HOPTO.ORG:23"
  "   ***   HACIEND EL BANANAS TELNET://HACIEND.BBS.FI:23   ***"
  " (SCROLLER WRAPS HERE)";

int main(int argc, char *argv[])
{
  SDL_Surface *screen, *combine, *img0, *img1, *img2, *img3, *img4, *img5;
  SDL_Surface *img6;
  SDL_Surface *noise0, *noise1, *noise2, *noise3, *noise4, *noise5;
  SDL_Surface *noise6, *noise7, *ramp_surface, *font;
  SDL_Surface **picptr[PICTOP + 1], **noiseptr[NOISETOP + 1];
  SDL_Event event;
  SDL_Rect myrect;
  Uint32 ticks, baseticks = 0, scrollticks, scrollbaseticks = 0, ramp = 0;
  unsigned int i, j, sinindex = 0, picindex = 0, noiseindex = 0;
  unsigned int  mode = RAMP_IT, ramp_it_counter = 36, ltime, *tmp, *sintab;
  double d;
  char picpath[] = "demodata/0.jpg";
  char soundpath[] = "demodata/san.ogg";

  ltime = time(NULL);
  srand(ltime);

  tmp = (unsigned int *)malloc(256 * sizeof(unsigned int));
  if(!tmp) exit(1);

  /* räkna ut sinusvärden */
  i = 0;
  for(d = 0.0; d <= 2 * M_PI; d = d + 2 * M_PI / 255) {
    tmp[i] = (unsigned int)(sin(d) * 128 + 128);
    i++;
  }

  sintab = (unsigned int *)malloc(256 * sizeof(unsigned int));
  if(!sintab) exit(1);

  /* kopiera och invertera den del av kurvan vi är intresserade av */
  j = 0;
  for(i=64; i < 192; i++) {
    sintab[j] = tmp[i];
    j++;
  }
  for(i=191; i >= 64; i--) {
    sintab[j] = tmp[i];
    j++;
  }

  free(tmp); /* nu behöver vi inte den längre */

  /* Initialize SDL */
  if(SDL_Init(SDL_INIT_VIDEO) < 0) {
    fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
    exit(1);
  }

  /* Initiera ljud */
  if (Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 1024) < 0) {
    fprintf(stderr, "Error initializing SDL_mixer: %s\n", Mix_GetError());
  }
  /* Ladda vårt ljud */
  fputs("Please stand by, loading sound... ", stderr);
  mysound = Mix_LoadWAV(soundpath);
  if(mysound == NULL)
    fputs("\nCould not load sound, running silent.\n", stderr);
  fputs("Proceeding.\n", stderr);

  /* Set a video mode */
  if(argc > 1 && !strcmp(argv[1], "-f"))
    screen = SDL_SetVideoMode(WIDTH, HEIGHT, DEPTH, VIDEOFLAGS|SDL_FULLSCREEN);
  else
    screen = SDL_SetVideoMode(WIDTH, HEIGHT, DEPTH, VIDEOFLAGS);
  if(screen == NULL) {
    fprintf(stderr, "Couldn't set display mode: %s\n", SDL_GetError());
    exit(1);
  }

  SDL_WM_SetCaption(demotitle, demotitle);
  SDL_ShowCursor(SDL_DISABLE);

  /* ordna en tom yta med default settings */
  combine = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT, DEPTH,
				 0, 0, 0, 0);
  ramp_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT, DEPTH,
				 0, 0, 0, 0);

  /* initiera pekare */
  picptr[0] = &img0;
  picptr[1] = &img1;
  picptr[2] = &img2;
  picptr[3] = &img3;
  picptr[4] = &img4;
  picptr[5] = &img5;
  picptr[6] = &img6;

  noiseptr[0] = &noise0;
  noiseptr[1] = &noise1;
  noiseptr[2] = &noise2;
  noiseptr[3] = &noise3;
  noiseptr[4] = &noise4;
  noiseptr[5] = &noise5;
  noiseptr[6] = &noise6;
  noiseptr[7] = &noise7;

  /* ladda bilder */
  for(i=0; i <= PICTOP; i++) {
    *picptr[i] = MyLoadImage(&picpath[0]);
    picpath[9]++;
  }

  /* ladda font */
  font = MyLoadImage("demodata/demofont.png");
  SDL_SetColorKey(font, SDL_SRCCOLORKEY|SDL_RLEACCEL, 0x555555);

  /* generera noiseytor */
  for(i=0; i <= NOISETOP; i++) {
    *noiseptr[i] = NoiseGenerator();
  }

  ltime = rand() % RAMPS; /* för färgen på RAMP_IT */

  /* Starta ljudet */
  if(mysound != NULL) {
    StartSound(0);
    Mix_ChannelFinished(&StartSound); /* loopar ljudet när det slutar */
  }
  do {
    if(SDL_MUSTLOCK(screen)) {
      if(SDL_LockSurface(screen) < 0) {
	SDL_Quit();
	exit(1);
      }
    }

    switch(mode) {
    case RAMP_IT:
      myrect.x = myrect.y = 0;
      myrect.w = WIDTH;
      myrect.h = HEIGHT;
      for(i=0; i<12; i++) {
	switch(ltime) {
	case 0:
	  SDL_FillRect(ramp_surface, &myrect,
		     SDL_MapRGB(ramp_surface->format, 255, ramp, ramp));
	  break;
	case 1:
	  SDL_FillRect(ramp_surface, &myrect,
		       SDL_MapRGB(ramp_surface->format, ramp, 255, ramp));
	  break;
	case 2:
	  SDL_FillRect(ramp_surface, &myrect,
		       SDL_MapRGB(ramp_surface->format, ramp, ramp, 255));
	  break;
	case 3:
	  SDL_FillRect(ramp_surface, &myrect,
		       SDL_MapRGB(ramp_surface->format, 255, ramp, 255));
	  break;
	case 4:
	  SDL_FillRect(ramp_surface, &myrect,
		       SDL_MapRGB(ramp_surface->format, ramp, 255, 255));
	  break;
	case 5:
	  SDL_FillRect(ramp_surface, &myrect,
		       SDL_MapRGB(ramp_surface->format, 255, 255, ramp));
	  break;
	case 6:
	  SDL_FillRect(ramp_surface, &myrect,
		       SDL_MapRGB(ramp_surface->format, ramp, ramp, ramp));
	}
	ramp += 20;
	if(ramp > 240) ramp = 0;
	myrect.x += 20;
	myrect.y += 20;
	myrect.w -= 40;
	myrect.h -= 40;
      }
      ramp_it_counter--;
      mode = WAIT;
      break;
    case NOISE:
      /* alphablenda noiset */
      SDL_SetAlpha(*noiseptr[noiseindex], SDL_SRCALPHA, sintab[sinindex]);
      if(sinindex < 255) sinindex++;
      if(sinindex < 255) sinindex++;
      else {
	sinindex = 0;
	if(picindex == 3 || picindex == PICTOP) {
	  if(picindex == PICTOP) picindex = 0;
	  else picindex++;
	  ramp_it_counter = 36; /* varvet runt, nu rampar vi lite */
	  ltime = rand() % RAMPS; /* ny färg för RAMP_IT */
	  /* rensa den gamla rampningen */
	  switch(ltime) {
	  case 0:
	    SDL_FillRect(ramp_surface, NULL,
			 SDL_MapRGB(ramp_surface->format, 255, ramp, ramp));
	    break;
	  case 1:
	    SDL_FillRect(ramp_surface, NULL,
			 SDL_MapRGB(ramp_surface->format, ramp, 255, ramp));
	    break;
	  case 2:
	    SDL_FillRect(ramp_surface, NULL,
			 SDL_MapRGB(ramp_surface->format, ramp, ramp, 255));
	    break;
	  case 3:
	    SDL_FillRect(ramp_surface, NULL,
			 SDL_MapRGB(ramp_surface->format, 255, ramp, 255));
	    break;
	  case 4:
	    SDL_FillRect(ramp_surface, NULL,
			 SDL_MapRGB(ramp_surface->format, ramp, 255, 255));
	    break;
	  case 5:
	    SDL_FillRect(ramp_surface, NULL,
			 SDL_MapRGB(ramp_surface->format, 255, 255, ramp));
	    break;
	  case 6:
	    SDL_FillRect(ramp_surface, NULL,
			 SDL_MapRGB(ramp_surface->format, ramp, ramp, ramp));
	  }
	}
	else picindex++; /* inte dags för RAMP_IT? ta nästa bild */
      }

      if(noiseindex < NOISETOP) noiseindex++;
      else noiseindex = 0;

      mode = WAIT;
      break;
    case WAIT:
      ticks = SDL_GetTicks();
      ticks -= baseticks;
      if(ticks >= TICKWAIT) {  /* om vi kör på en superdator kan det */
	baseticks += ticks; /* vara bra med en liten fördröjning ;) */
	if(ramp_it_counter) mode = RAMP_IT;
	else mode = NOISE;
      }
      break;
    }
    if(!ramp_it_counter) {
      SDL_BlitSurface(*picptr[picindex], NULL, combine, NULL);
      SDL_BlitSurface(*noiseptr[noiseindex], NULL, combine, NULL);
    }
    else SDL_BlitSurface(ramp_surface, NULL, combine, NULL);
    scrollticks = SDL_GetTicks();  /*scrollern har sin egen timer för att */
    scrollticks -= scrollbaseticks; /* den ska flyta i konstant fart */
    if(scrollticks >= SCROLLWAIT) {
      scrollbaseticks += scrollticks;
      Update_Scroller(font, combine, TRUE);
    }
    else Update_Scroller(font, combine, FALSE);
      
    SDL_BlitSurface(combine, NULL, screen, NULL);
    if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
    SDL_Flip(screen);
    SDL_PollEvent(&event);
    if(event.key.keysym.sym == SDLK_F1) SDL_WM_ToggleFullScreen(screen);
  } while(event.key.keysym.sym != SDLK_ESCAPE);

  SDL_Quit();
  free(sintab);

  exit(0);
}


/* Laddar bilden i image_path och returnerar en pekare till en surface med */
/* denna bild i. */
SDL_Surface *MyLoadImage(char *image_path)
{
  SDL_Surface *image = IMG_Load(image_path);
  if(!image) {
    printf("IMG_Load: %s\n", IMG_GetError());
    SDL_Quit();
    exit(1);
  }
  else return image;
}


SDL_Surface *NoiseGenerator(void)
{
  Uint32 shade, *bufp;
  unsigned int i, x, y, ytimesw;
  SDL_Surface *surface = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT,
					      DEPTH, 0, 0, 0, 0);

  if(surface == NULL) {
    fprintf(stderr, "CreateRGBSurface failed: %s\n", SDL_GetError());
    SDL_Quit();
    exit(1);
  }

  /* rita noise */
  for (x=0; x<WIDTH; x++) {
    for (y=0; y<HEIGHT; y++) {
      i = rand() % 255;
      /* shade = SDL_MapRGB(surface->format, shade, shade, shade); */
      shade = i << 16; /* samma grej fast snabbare */
      shade = shade | (i << 8);
      shade = shade | i;
      ytimesw = y * surface->pitch / BPP;
      bufp = (Uint32 *)surface->pixels + ytimesw + x;
      *bufp = shade;
    }
  }

  return surface;
}


void Update_Scroller(SDL_Surface *font, SDL_Surface *dest_surface,
		     Uint8 movement)
{
  SDL_Rect rcSrc, rcDest;
  unsigned int i, tmp_scrollpos;

  tmp_scrollpos = scrollpos;
  rcSrc.w = rcSrc.h = 16;
  rcDest.w = rcDest.h = rcDest.x = 0;
  rcDest.y = SCROLL_Y;

  for(i=0; i <= 40; i++) {
    if(scrolltext[tmp_scrollpos] == '\0') {
      tmp_scrollpos = 0;
    }
    rcSrc.x = ((scrolltext[tmp_scrollpos] - 32) % 16) * 16;
    rcSrc.y = ((scrolltext[tmp_scrollpos] - 32) / 16) * 16;
    rcDest.x = (i * 16) - finscroll;
    tmp_scrollpos++;

    SDL_BlitSurface(font, &rcSrc, dest_surface, &rcDest);
  }

  /* flytta scrollern bara varje SCROLLWAIT frame */
  if(movement) {
    if(finscroll < 15) finscroll++;
    if(finscroll < 15) finscroll++;
    else {
      finscroll = 0;
      scrollpos++;
      /* Null markerar slutet av scrolltexten */
      if(scrolltext[scrollpos] == '\0') {
	scrollpos = 0;
      }
    }
  }

}


void StartSound(int channel)
{
  Mix_PlayChannel(channel, mysound, 0); 
}
